package com.core.cloudcommon.security;

import com.alibaba.fastjson.JSON;
import com.core.cloudcommon.CommonCustomException;
import com.core.cloudcommon.redis.service.CloudRedisTemplateService;
import com.core.cloudcommon.redis.vo.CloudRedisVO;
import com.core.cloudcommon.enums.CloudSystemLoginResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.security.SecureRandom;
import java.util.*;

/**
 * @author =====Lqj=====
 * @className SmsCodeManager
 * @description 短信验证码管理器
 * @create 2024/12/16 17:00
 **/
@Slf4j
@Component
public class SmsCodeManager {

    @Autowired
    private CloudRedisTemplateService cloudRedisTemplateService;

    // Redis Key前缀
    private static final String SMS_CODE_KEY = "sms_code:";
    private static final String SMS_SEND_LIMIT_KEY = "sms_send_limit:";
    private static final String SMS_VERIFY_FAIL_KEY = "sms_verify_fail:";
    private static final String SMS_IP_LIMIT_KEY = "sms_ip_limit:";

    // 配置参数
    @Value("${sms.code.length:6}")
    private int codeLength;

    @Value("${sms.code.expire:300}")
    private long codeExpireSeconds; // 5分钟

    @Value("${sms.send.daily-limit:10}")
    private int dailySendLimit;

    @Value("${sms.send.interval:60}")
    private long sendIntervalSeconds; // 1分钟间隔

    @Value("${sms.verify.max-fail:3}")
    private int maxVerifyFail;

    @Value("${sms.ip.daily-limit:50}")
    private int ipDailyLimit;

    /**
     * 验证码信息
     */
    public static class SmsCodeInfo {
        private String code;
        private String phone;
        private String ip;
        private Date createTime;
        private Date expireTime;
        private int verifyFailCount;
        private boolean used;

        // 构造函数和getter/setter
        public SmsCodeInfo() {}

        public SmsCodeInfo(String code, String phone, String ip) {
            this.code = code;
            this.phone = phone;
            this.ip = ip;
            this.createTime = new Date();
            this.expireTime = new Date(System.currentTimeMillis() + 300 * 1000); // 5分钟后过期
            this.verifyFailCount = 0;
            this.used = false;
        }

        // Getters and Setters
        public String getCode() { return code; }
        public void setCode(String code) { this.code = code; }
        
        public String getPhone() { return phone; }
        public void setPhone(String phone) { this.phone = phone; }
        
        public String getIp() { return ip; }
        public void setIp(String ip) { this.ip = ip; }
        
        public Date getCreateTime() { return createTime; }
        public void setCreateTime(Date createTime) { this.createTime = createTime; }
        
        public Date getExpireTime() { return expireTime; }
        public void setExpireTime(Date expireTime) { this.expireTime = expireTime; }
        
        public int getVerifyFailCount() { return verifyFailCount; }
        public void setVerifyFailCount(int verifyFailCount) { this.verifyFailCount = verifyFailCount; }
        
        public boolean isUsed() { return used; }
        public void setUsed(boolean used) { this.used = used; }

        public boolean isExpired() {
            return new Date().after(expireTime);
        }

        public void incrementFailCount() {
            this.verifyFailCount++;
        }
    }

    /**
     * 生成并发送验证码
     * @param phone 手机号
     * @param ip 客户端IP
     * @param businessType 业务类型
     * @return 验证码（测试环境返回，生产环境不返回）
     */
    public String generateAndSendCode(String phone, String ip, String businessType) {
        // 检查发送限制
        checkSendLimits(phone, ip);

        // 生成验证码
        String code = generateSecureCode();

        // 创建验证码信息
        SmsCodeInfo codeInfo = new SmsCodeInfo(code, phone, ip);

        // 保存到Redis
        String key = SMS_CODE_KEY + businessType + ":" + phone;
        cloudRedisTemplateService.set(new CloudRedisVO(key, JSON.toJSONString(codeInfo), codeExpireSeconds));

        // 记录发送次数
        recordSendAttempt(phone, ip);

        // 这里应该调用短信服务发送验证码
        sendSmsCode(phone, code, businessType);

        log.info("验证码发送成功，手机号: {}, IP: {}, 业务类型: {}", phone, ip, businessType);

        // 测试环境返回验证码，生产环境不返回
        return isTestEnvironment() ? code : null;
    }

    /**
     * 验证验证码
     * @param phone 手机号
     * @param inputCode 输入的验证码
     * @param businessType 业务类型
     * @return true-验证成功，false-验证失败
     */
    public boolean verifyCode(String phone, String inputCode, String businessType) {
        String key = SMS_CODE_KEY + businessType + ":" + phone;
        Object codeObj = cloudRedisTemplateService.get(new CloudRedisVO(key));

        if (codeObj == null) {
            throw new CommonCustomException(CloudSystemLoginResultCode.CODE200008.getCode(), 
                "验证码已过期或不存在，请重新获取");
        }

        try {
            SmsCodeInfo codeInfo = JSON.parseObject(codeObj.toString(), SmsCodeInfo.class);

            // 检查是否过期
            if (codeInfo.isExpired()) {
                cloudRedisTemplateService.del(key);
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200008.getCode(), 
                    "验证码已过期，请重新获取");
            }

            // 检查是否已使用
            if (codeInfo.isUsed()) {
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200009.getCode(), 
                    "验证码已使用，请重新获取");
            }

            // 检查失败次数
            if (codeInfo.getVerifyFailCount() >= maxVerifyFail) {
                cloudRedisTemplateService.del(key);
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200019.getCode(), 
                    "验证码错误次数过多，请重新获取");
            }

            // 验证码码
            if (!codeInfo.getCode().equals(inputCode)) {
                codeInfo.incrementFailCount();
                cloudRedisTemplateService.set(new CloudRedisVO(key, JSON.toJSONString(codeInfo), 
                    cloudRedisTemplateService.getExpire(new CloudRedisVO(key))));
                
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200009.getCode(), 
                    "验证码不正确，剩余尝试次数: " + (maxVerifyFail - codeInfo.getVerifyFailCount()));
            }

            // 验证成功，标记为已使用
            codeInfo.setUsed(true);
            cloudRedisTemplateService.set(new CloudRedisVO(key, JSON.toJSONString(codeInfo), 
                cloudRedisTemplateService.getExpire(new CloudRedisVO(key))));

            log.info("验证码验证成功，手机号: {}", phone);
            return true;

        } catch (CommonCustomException e) {
            throw e;
        } catch (Exception e) {
            log.error("验证码验证失败: {}", e.getMessage(), e);
            throw new CommonCustomException(CloudSystemLoginResultCode.CODE200009.getCode(), 
                "验证码验证失败");
        }
    }

    /**
     * 检查发送限制
     */
    private void checkSendLimits(String phone, String ip) {
        // 检查发送间隔
        checkSendInterval(phone);

        // 检查每日发送次数限制
        checkDailySendLimit(phone);

        // 检查IP每日发送次数限制
        checkIpDailySendLimit(ip);
    }

    /**
     * 检查发送间隔
     */
    private void checkSendInterval(String phone) {
        String key = SMS_SEND_LIMIT_KEY + "interval:" + phone;
        Object lastSend = cloudRedisTemplateService.get(new CloudRedisVO(key));
        
        if (lastSend != null) {
            long lastSendTime = Long.parseLong(lastSend.toString());
            long now = System.currentTimeMillis();
            long interval = (now - lastSendTime) / 1000;
            
            if (interval < sendIntervalSeconds) {
                long remainingSeconds = sendIntervalSeconds - interval;
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200006.getCode(), 
                    String.format("发送过于频繁，请%d秒后重试", remainingSeconds));
            }
        }
    }

    /**
     * 检查每日发送次数限制
     */
    private void checkDailySendLimit(String phone) {
        String key = SMS_SEND_LIMIT_KEY + "daily:" + phone;
        Object count = cloudRedisTemplateService.get(new CloudRedisVO(key));
        
        if (count != null) {
            int sendCount = Integer.parseInt(count.toString());
            if (sendCount >= dailySendLimit) {
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200021.getCode(), 
                    String.format("今日发送次数已达上限(%d次)，请明天再试", dailySendLimit));
            }
        }
    }

    /**
     * 检查IP每日发送次数限制
     */
    private void checkIpDailySendLimit(String ip) {
        String key = SMS_IP_LIMIT_KEY + "daily:" + ip;
        Object count = cloudRedisTemplateService.get(new CloudRedisVO(key));
        
        if (count != null) {
            int sendCount = Integer.parseInt(count.toString());
            if (sendCount >= ipDailyLimit) {
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200021.getCode(), 
                    "该IP今日发送次数已达上限，请明天再试");
            }
        }
    }

    /**
     * 记录发送尝试
     */
    private void recordSendAttempt(String phone, String ip) {
        long now = System.currentTimeMillis();
        
        // 记录发送间隔
        String intervalKey = SMS_SEND_LIMIT_KEY + "interval:" + phone;
        cloudRedisTemplateService.set(new CloudRedisVO(intervalKey, String.valueOf(now), sendIntervalSeconds));
        
        // 记录每日发送次数
        String dailyKey = SMS_SEND_LIMIT_KEY + "daily:" + phone;
        Object count = cloudRedisTemplateService.get(new CloudRedisVO(dailyKey));
        int sendCount = count != null ? Integer.parseInt(count.toString()) : 0;
        sendCount++;
        
        // 计算到明天0点的秒数
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        long expireSeconds = (calendar.getTimeInMillis() - now) / 1000;
        
        cloudRedisTemplateService.set(new CloudRedisVO(dailyKey, String.valueOf(sendCount), expireSeconds));
        
        // 记录IP每日发送次数
        String ipDailyKey = SMS_IP_LIMIT_KEY + "daily:" + ip;
        Object ipCount = cloudRedisTemplateService.get(new CloudRedisVO(ipDailyKey));
        int ipSendCount = ipCount != null ? Integer.parseInt(ipCount.toString()) : 0;
        ipSendCount++;
        cloudRedisTemplateService.set(new CloudRedisVO(ipDailyKey, String.valueOf(ipSendCount), expireSeconds));
    }

    /**
     * 生成安全的验证码
     */
    private String generateSecureCode() {
        SecureRandom random = new SecureRandom();
        StringBuilder code = new StringBuilder();
        
        for (int i = 0; i < codeLength; i++) {
            code.append(random.nextInt(10));
        }
        
        return code.toString();
    }

    /**
     * 发送短信验证码
     * 这里应该集成真实的短信服务
     */
    private void sendSmsCode(String phone, String code, String businessType) {
        // 这里应该调用短信服务API
        // 例如：阿里云短信、腾讯云短信等
        
        log.info("【模拟发送短信】手机号: {}, 验证码: {}, 业务类型: {}", phone, code, businessType);
        
        // 实际项目中的实现示例：
        // smsService.sendCode(phone, code, getTemplateByBusinessType(businessType));
    }

    /**
     * 判断是否为测试环境
     */
    private boolean isTestEnvironment() {
        // 这里可以根据配置文件或环境变量判断
        return true; // 简化处理，实际项目中应该从配置读取
    }

    /**
     * 清理过期的验证码
     */
    public void cleanExpiredCodes() {
        // 这里可以实现定时清理过期验证码的逻辑
        log.info("清理过期验证码任务执行");
    }

    /**
     * 获取验证码信息（用于管理后台）
     */
    public SmsCodeInfo getCodeInfo(String phone, String businessType) {
        String key = SMS_CODE_KEY + businessType + ":" + phone;
        Object codeObj = cloudRedisTemplateService.get(new CloudRedisVO(key));
        
        if (codeObj != null) {
            try {
                return JSON.parseObject(codeObj.toString(), SmsCodeInfo.class);
            } catch (Exception e) {
                log.error("解析验证码信息失败: {}", e.getMessage(), e);
            }
        }
        
        return null;
    }
}
